package com.example.demo.annotation;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author
 * @Description
 */
public class MyRequestBodyResolver implements HandlerMethodArgumentResolver {
    private static final String JSON_BODY_ATTRIBUTE = "JSON_REQUEST_BODY";

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(MyRequestBody.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String json = getRequestBody(webRequest);
        JSONObject jsonObject = JSON.parseObject(json);
        //
        // value作为json的key
        MyRequestBody annotation = parameter.getParameterAnnotation(MyRequestBody.class);
        String key = StringUtils.isEmpty(annotation.key()) ? parameter.getParameterName() : annotation.key();
        Object object = null;

        if (StringUtils.isEmpty(key)) {// 未设置key
            key = parameter.getParameterName();
        }
        object = jsonObject.get(key);
        //
        // 获取 类型
        Class<?> parameterClass = parameter.getParameterType();
        if (object != null) {
//            System.err.println("@ " + object);
            Object o;
            if (parameterClass == String.class){
                o = object.toString();
            } else {
                o = JSON.parseObject(object.toString(), parameterClass);
            }
            // 直接转换字符串会异常
//            Object o = JSON.parseObject(object.toString(), parameterClass);
//            System.err.println("return " + o);
            return o;
        }
        //
        if (parameterClass == String.class) {
            if (annotation.required()) {
                throw new IllegalArgumentException("所需参数" + key + "不存在");
            } else {
                return null;
            }
        }
        // 基础数据类型
        if (isBasicType(parameterClass) || isBasicPackagingType(parameterClass)) {
            //
            if (annotation.required()) {
                // 必须 未解析到 就异常
                throw new IllegalArgumentException("所需参数" + key + "不存在");
            } else {
                // 非必须 未解析到视为null
                return null;
            }
        }
        //
        // 对应value解析不到 解析整个json
        if (annotation.parseAll()) { // 允许解析外层
            object = JSON.parseObject(jsonObject.toString(), parameterClass);

            // 非必要参数 直接返回，必要参数 没有值则异常
            if (annotation.required()) {
                boolean b = false;
                Field[] fields = parameterClass.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    // 基本数据类型存在私有化字段 存在异常风险 恒为true
                    if (field.get(object) != null) {
                        b = true;
                        break;
                    }
                }
                if (!b) { // 数据类型和参数类型不符
                    throw new IllegalArgumentException("所需参数" + key + "不存在");
                }
            }
            System.err.println("return " + object);
            return object;

        } else { // 不允许解析外层
            // 必传 则异常
            if (annotation.required()) {
                throw new IllegalArgumentException("所需参数" + key + "不存在");
            }
            return null;
        }
    }

    private String getRequestBody(NativeWebRequest webRequest) {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);

        Object json = webRequest.getAttribute(JSON_BODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);

        if (json == null) {
            try {
                json = IOUtils.toString(request.getReader());
                webRequest.setAttribute(JSON_BODY_ATTRIBUTE, json, NativeWebRequest.SCOPE_REQUEST);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return (String) json;
    }

    private boolean isBasicType(Class<?> clazz) {
        Set<Class<?>> set = new HashSet<>();
        set.add(Integer.class);
        set.add(Long.class);
        set.add(Short.class);
        set.add(Float.class);
        set.add(Double.class);
        set.add(Boolean.class);
        set.add(Byte.class);
        set.add(Character.class);
        return set.contains(clazz);
    }

    private boolean isBasicPackagingType(Class<?> clazz) {
        Set<String> set = new HashSet<>();
        set.add("int");
        set.add("short");
        set.add("long");
        set.add("float");
        set.add("double");
        set.add("boolean");
        set.add("byte");
        set.add("char");
        return set.contains(clazz.getName());
    }

    private Object parseBasicPackagingType(Class<?> clazz, Object value) {

        if (clazz == Integer.class) {
            return Integer.valueOf(value.toString());
        } else if (clazz == Short.class) {
            return Short.valueOf(value.toString());
        } else if (clazz == Long.class) {
            return Long.valueOf(value.toString());
        } else if (clazz == Float.class) {
            return Float.valueOf(value.toString());
        } else if (clazz == Double.class) {
            return Double.valueOf(value.toString());
        } else if (clazz == Byte.class) {
            return Byte.valueOf(value.toString());
        } else if (clazz == Boolean.class) {
            return value.toString();
        } else if (clazz == Character.class) {
            return value.toString().charAt(0);
        }
        return null;

    }

    private Object parseBasicType(Class<?> clazz, Object value) {
        return switch (clazz.getName()) {
            case "boolean" -> Boolean.valueOf(value.toString());
            case "int" -> Integer.valueOf(value.toString());
            case "short" -> Short.valueOf(value.toString());
            case "long" -> Long.valueOf(value.toString());
            case "float" -> Float.valueOf(value.toString());
            case "double" -> Double.valueOf(value.toString());
            case "char" -> value.toString().charAt(0);
            case "byte" -> Byte.valueOf(value.toString());
            default -> null;
        };
    }
}
